#ifndef AST_HPP
#define AST_HPP

#include <cstdlib>
#include <vector>
using namespace std;

class Token;
class Object;
class IntObject;
class StringObject;
class CharObject;
class Env;
class StackFrame;
class Type;
class Visitor;

class Node {
public:
    Node() {}
    virtual ~Node() {}

    virtual void accept(Visitor* visitor);
};

class BinaryOp : public Node {
protected:
    Node* _left;
    Node* _right;

public:
    BinaryOp(Node* left, Node* right) : _left(left), _right(right) {}
    ~BinaryOp();

    Node* left()  { return _left; }
    Node* right() { return _right; }
};

class BitOrNode : public BinaryOp {
public:
    BitOrNode(Node* left, Node* right) : BinaryOp(left, right) {
    }

    virtual void accept(Visitor* visitor);
};

class BitXorNode : public BinaryOp {
public:
    BitXorNode(Node* left, Node* right) : BinaryOp(left, right) {
    }

    virtual void accept(Visitor* visitor);
};

class BitAndNode : public BinaryOp {
public:
    BitAndNode(Node* left, Node* right) : BinaryOp(left, right) {
    }

    virtual void accept(Visitor* visitor);
};

class LeftShiftNode : public BinaryOp {
public:
    LeftShiftNode(Node* left, Node* right) : BinaryOp(left, right) {
    }

    virtual void accept(Visitor* visitor);
};

class RightShiftNode : public BinaryOp {
public:
    RightShiftNode(Node* left, Node* right) : BinaryOp(left, right) {
    }

    virtual void accept(Visitor* visitor);
};

class AddNode : public BinaryOp {
public:
    AddNode(Node* left, Node* right) : BinaryOp(left, right) {
    }

    virtual void accept(Visitor* visitor);
};

class SubNode : public BinaryOp {
public:
    SubNode(Node* left, Node* right) : BinaryOp(left, right) {
    }

    virtual void accept(Visitor* visitor);
};

class MulNode : public BinaryOp {
public:
    MulNode(Node* left, Node* right) : BinaryOp(left, right) {
    }

    virtual void accept(Visitor* visitor);
};

class DivNode : public BinaryOp {
public:
    DivNode(Node* left, Node* right) : BinaryOp(left, right) {
    }

    virtual void accept(Visitor* visitor);
};

class ListNode : public Node {
private:
    vector<Node*>* _node_list;

public:
    ListNode();
    virtual ~ListNode();

    void add(Node* n);
    vector<Node*>* node_list() { return _node_list; }

    virtual void accept(Visitor* visitor);
};

class ConstInt : public Node {
private:
    IntObject* _value;

public:
    ConstInt(IntObject* v) : _value(v) {};
    ~ConstInt();

    IntObject* value() { return _value; }
    virtual void accept(Visitor* visitor);
};

class ConstBool : public Node {
private:
    bool _value;

public:
    ConstBool(bool v) : _value(v) {};
    ~ConstBool() {};

    bool value() { return _value; }
    virtual void accept(Visitor* visitor);
};

class ConstString : public Node {
private:
    StringObject* _value;

public:
    ConstString(StringObject* v) : _value(v) {};
    ~ConstString();

    StringObject* value() { return _value; }
    virtual void accept(Visitor* visitor);
};

class ConstChar : public Node {
private:
    CharObject* _value;

public:
    ConstChar(CharObject* v) : _value(v) {};
    ~ConstChar();
    CharObject* value() { return _value; }
    virtual void accept(Visitor* visitor);
};

class VarNode : public Node {
private:
    char* _name;

public:
    VarNode(Token* name);
    ~VarNode();

    const char* name() { return _name; }

    virtual void accept(Visitor* visitor);
};

// AST Node for "var x : Type"
class VarDefNode : public Node {
private:
    char* _name;
    Type* _type;
    Node* _init;

public:
    VarDefNode(Token* name);
    ~VarDefNode();

    const char* name() { return _name; }
    Type* type()       { return _type; }
    Node* init_value() { return _init; }

    void set_type(Type* t) { _type = t; }
    void set_init(Node* i) { _init = i; }

    virtual void accept(Visitor* visitor);
};


class AssignNode : public BinaryOp {
public:
    AssignNode(Node* left, Node* right):
        BinaryOp(left, right) {}

    virtual void accept(Visitor* visitor);
};

class LambdaDef : public Node {
private:
    VarDefNode*       _param;
    Node*          _body;

public:
    LambdaDef(VarDefNode* param, Node* body):
        _param(param), _body(body){}
    ~LambdaDef();

    VarDefNode* param() { return _param; }
    Node*       body()  { return _body; }

    virtual void accept(Visitor* visitor);
};

class CallNode : public Node {
private:
    Node*    _func_name;
    Node*    _param;

public:
    CallNode(Node* func_name, Node* param) :
        _func_name(func_name), _param(param) {}
    ~CallNode();
    
    Node* func_name() { return _func_name; }
    Node* param()     { return _param; }

    virtual void accept(Visitor* visitor);
};

class PrintNode : public Node {
private:
    Node*   _body;

public:
    PrintNode(Node* body) : _body(body) {}
    ~PrintNode();

    Node* body() { return _body; }
    virtual void accept(Visitor* visitor);
};

class PrintlnNode : public Node {
private:
    Node*   _body;

public:
    PrintlnNode(Node* body) : _body(body) {}
    ~PrintlnNode();

    Node* body() { return _body; }
    virtual void accept(Visitor* visitor);
};

class IfNode : public Node {
private:
    Node* _cond;
    Node* _then;
    Node* _else;

public:
    IfNode(Node* cond, Node* left, Node* right) :
        _cond(cond), _then(left), _else(right) {}
    ~IfNode();
    
    Node* cond()       { return _cond; }
    Node* then_block() { return _then; }
    Node* else_block() { return _else; }

    virtual void accept(Visitor* visitor);
};

enum CmpOp {
    N_EQUAL,
};

class CmpNode : public BinaryOp {
private:
    CmpOp _op;

public:
    CmpNode(CmpOp op, Node* left, Node* right) :
            BinaryOp(left, right), _op(op) {}

    CmpOp cmp_op() { return _op; }

    virtual void accept(Visitor* visitor);
};

class LogicOrNode: public BinaryOp {
public:
    LogicOrNode(Node* left, Node* right) : BinaryOp(left, right) {
    }

    virtual void accept(Visitor* visitor);
};

class LogicAndNode: public BinaryOp {
public:
    LogicAndNode(Node* left, Node* right) : BinaryOp(left, right) {
    }

    virtual void accept(Visitor* visitor);
};

class LogicNotNode: public Node {
private:
    Node* _value;
public:
    LogicNotNode(Node* v) : _value(v) {
    }

    Node* value() { return _value; }

    virtual void accept(Visitor* visitor);
};

/*
 * Nodes for type defination.
 */
class TypeDefNode : public Node {
private:
    char*   _name;
    Type*   _type;

public:
    TypeDefNode(Token* t);
    ~TypeDefNode();

    const char* name() { return _name; }
    Type* def_type()   { return _type; }
    void set_type(Type* t) { _type = t; }

    virtual void accept(Visitor* visitor);
};

#endif

